// The gameboard instance
var board = "dummy";

// The "difficulty" between 1 and 5.  This number will be
// used to randomly remove the difficulty number of cells from
// each region.  It should be noted that since this is a random
// removal that the difficulty may actually vary wildly based
// on other factors.
var difficulty = 3;

// Randomly shuffle the values in the specified Array instance
function shuffle(anArray) {
	if (anArray.length > 1) {
		var arrayLength = anArray.length;
		var rounds = arrayLength / 2;
		
		for (var i = 0; i < rounds; i = i + 1) {
			var index1 = Math.random(arrayLength);
			var index2 = Math.random(arrayLength);
			
			var temp = anArray[index1];
			anArray[index1] = anArray[index2];
			anArray[index2] = temp;
		}
	}
}

/* ---------------------------------------------
 * Prototype Definition for type Cell
 * --------------------------------------------- */
cell.prototype = new TextBox(); 

// Setter method that checks the "changeability" of the cell
cell.prototype.setValue = function(newValue) {
	if (this.changeable) {
		this.value = newValue;
	
// CW: COMMENTED OUT THIS FEATURE SINCE TIMER NO LONGER SUPPORTS THE "count" ATTRIBUTE	
//		if (!board.building) {
//			var conflictingCells = board.findConflictingCells(this);
			
//			var cellCount = conflictingCells.length;
//			for (int i = 0; i < cellCount; i = i + 1) {
//				conflictingCells[i].class = "conflicting";
//			}
			
//			if (cellCount > 0) {
//				var timer = new Timer();
//				timer.cells = conflictingCells;
//				timer.interval = 500;
//				timer.delay = 500;
//				timer.count = 1;
//				timer.run = function() {
//					for (var i = 0; i < this.cells.length; i = i + 1) {
//						this.cells[i].class = "normal";
//					}
//				};
//				timer.start();
//			}

// CW: The following technique should work instead, once the new painting settles
//		if (!board.building) {
//			var conflictingCells = board.findConflictingCells(this);
//			
//			var cellCount = conflictingCells.length;
//			for (int i = 0; i < cellCount; i = i + 1) {
//				conflictingCells[i].class = "conflicting";
//			}
//			// force a paint
//			Moblet.repaint();
//
//			// wait 2 seconds
//			var date = new Date();
//			var curDate = new Date();
//			while(curDate.getTime()-date.getTime() < 2000) {
//				curDate = new Date();
//			}
//			
//			//set back to normal
//			for (int i = 0; i < cellCount; i = i + 1) {
//				conflictingCells[i].class = "normal";
//			}
//			// force a paint
//			Moblet.repaint();
//		}

		}
//	}
};

// Cell Constructor
function cell(row, col) {
	// Visual aspects of the cell
	this.name = "cell";
	this.halign = "center";
	this.valign = "center"; 
	this.class = "normal"; 
	this.width = 15; 
	this.height = 15;
	
	// Functional aspects of the cell
	this.changeable = true;
	this.row = row;
	this.col = col;
	this.value = " ";
}

/* ---------------------------------------------
 * Prototype Definition for a 3x3 Region of cells
 * --------------------------------------------- */
region.prototype = new Box();

// Return an array of cells that are part of this region
region.prototype.getRegionCells = function() {
	var regionCells = new Array();
	
	for (var regionrow = 0; regionrow < 3; regionrow = regionrow + 1) {
		for (var regioncol = 0; regioncol < 3; regioncol = regioncol + 1) {
			regionCells.push(this.children[regionrow].children[regioncol]);
		}
	}
	
	return regionCells;
};

// Region constructor
// Requires the *region* row and column
function region() {
	// Visual aspects of the region
	this.name = "region";
	this.class = "region";
	this.halign = "center";
	this.valign = "center"; 
	this.layout = "vertical";
	
	// Each region consists of 3 rows of 3 children.  Add the
	// child rows now.
	for (var regionrow = 0; regionrow < 3; regionrow = regionrow + 1) {
		Box rowBox = new Box();
		rowBox.layout = "horizontal";
		this.children.push(rowBox);
	}
}

/* ---------------------------------------------
 * Prototype Definition for the complete gameboard
 * --------------------------------------------- */
gameboard.prototype = new Box();

// Increment the value of the current cell.  Wraps
// around at 9 back to 0.
gameboard.prototype.clearCurrent = function() {
	this.current.setValue(" ");
};

// Create a new board for the player to attempt
// to solve
gameboard.prototype.createPuzzle = function() {
	show(building);
	board.createPuzzleTimed();
	hide(building);
	show(main);
};

// Create a new board for the player to attempt
// to solve
gameboard.prototype.createPuzzleTimed = function() {
	// Clear the board...
	for (var i = 0; i < 81; i = i + 1) {
		this.cells[i].value = " ";
		this.cells[i].changeable = true;
		this.cells[i].class = "hidden";
	}
	
	// A board is created, by solving the puzzle
	// to create a valid board and then hiding
	// various random cells to make an actual 
	// puzzle out of the board
	this.building = true;
	if (!this.solve()) {
		alert("Unable to create puzzle");
	}
	this.building = false;
	
	// Hide random cells in each region
	this.clearRandomCells();
	
	// Non-blank cells should not be changeable by the user
	for (var i = 0; i < 81; i = i + 1) {
		if (this.cells[i].value != " ") {
			this.cells[i].changeable = false;
		}
		
		this.cells[i].class = "normal";
		this.cells[i].focused = false;
	}
	

	
	// Set the current cell information
	this.current = this.getCellAt(0, 0);
	this.current.focused = true;
	main.focus = this.current;
	
	// Switch the role of the right softkey to allow it to
	// clear the cell value
	main.softkeys.rightSoft.value = "Clear Cell";
	main.onRightSoftkey = function() {
		board.clearCurrent();
	};
};

// Clear random cells in each region to
// make the current board into an actual puzzle
gameboard.prototype.clearRandomCells = function() {
	var indices = new Array();
	for (var i = 0; i < 9; i = i + 1) {
		indices.push(i);
	}
	
	// Try to distribute the blank cells throughout the table
	for (var regionIndex = 0; regionIndex < 9; regionIndex = regionIndex + 1) {
		shuffle(indices);
		
		var region = this.children[regionIndex / 3].children[regionIndex % 3];
		var regionCells = region.getRegionCells();
		
		for (var index = 0; index < difficulty; index = index + 1) {
			regionCells[indices[index]].value = " ";
		}
	}
};

// Shift the focus cell up on row.  
// Wrap around to the top if we are at the bottom
gameboard.prototype.down = function() {
	var row = this.current.row + 1;
	if (row > 8) row = 0;
	this.moveCurrentTo(row, this.current.col);
};

// Search through all of the cells, searching for the 
// cell with the fewest possible values.  Returns the
// found cell or null if no cell with possible values
// exists
gameboard.prototype.findCellWithFewestPossibleValues = function() {
	var minLength = 10;
	var theCell = null;
	
	for (var i = 0; i < 81 && minLength > 0; i = i + 1) {
		// Skip cells that already have a value
		if (this.cells[i].value == " ") {
			var possiblesLength = this.getPossibleValues(this.cells[i]).length;
			if (possiblesLength < minLength) {
				minLength = possiblesLength;
				theCell = this.cells[i];
			}
		}
	}
	
	return theCell;
};

// Return an array of cells that have conflicting values
// with the specified cell's value
gameboard.prototype.findConflictingCells = function(cell) {
	var conflicts = new Array();
	
	if (cell.value != " ") {
		// Test against the region cells
		var regionCells = this.getRegionAt(cell.row, cell.col).getRegionCells();
		for (var i = 0; i < 9; i = i + 1) {
			var c = regionCells[i];
			if (c.row != cell.row && c.col != cell.col && c.value == cell.value) {
				conflicts.push(c);
			}
		}
		
		// Now, check through the row and column that the
		// cell lives in
		for (var i = 0; i < 9; i = i + 1) {
			if (i != cell.row) {
				var c = this.getCellAt(i, cell.col);
				if (c.value == cell.value) {
					conflicts.push(c);
				}
			}
			
			if (i != cell.col) {
				var c = this.getCellAt(cell.row, i);
				if (c.value == cell.value) {
					conflicts.push(c);
				}
			}
		}
	}	
	
	return conflicts;
};

// Return the Cell at the specified location
gameboard.prototype.getCellAt = function(row, col) {
	return this.cells[row * 9 + col];
};

// Return the possible values for the specified cell.
gameboard.prototype.getPossibleValues = function(aCell) {
	// Start with an array of booleans to indicate 
	// possible values
	var possible = new Array();
	for (var i = 0; i < 9; i = i + 1) {
		possible.push(true);
	}
	
	// Remove the values that are no longer available.
	var regionCells = this.getRegionAt(aCell.row, aCell.col).getRegionCells();
	for (var i = 0; i < 9; i = i + 1) {
		// Values in the column
		if (i != aCell.row) {
			var c = this.getCellAt(i, aCell.col);
			if (c.value != null) {
				var index = c.value.toNumber();
				if (index > 0) {
					possible[index - 1] = false;
				}
			}
		}
		
		// Values in the row	
		if (i != aCell.col) {
			var c = this.getCellAt(aCell.row, i);
			if (c.value != null) {
				var index = c.value.toNumber();
				if (index > 0) {
					possible[index - 1] = false;
				}
			}
		}
		
		// Values in the region
		var regionCell = regionCells[i];
		if (regionCell.row != aCell.row && regionCell.col != aCell.col) {
			if (regionCell.value != null) {
				var index = regionCell.value.toNumber();
				if (index > 0) {
					possible[index - 1] = false;
				}
			}
		}
	}
	
	var possibleValues = new Array();
	for (var i = 0; i < 9; i = i + 1) {
		if (possible[i] == true) {
			possibleValues.push(i + 1);
		}
	}
	
	return possibleValues;
};

// Return the Region at the specified global location
gameboard.prototype.getRegionAt = function(row, col) {
	return this.children[row / 3].children[col / 3];
};

// Handle an arbitrary key press
gameboard.prototype.keyPressed = function(event) {
	if (event.type == 2) {
		if (event.label != 0) {
			this.current.setValue(event.label);
		}
	}
};

// Shift the focus cell left one column.  
// Wrap around to the right if we are at the far left.
gameboard.prototype.left = function() {
	var col = this.current.col - 1;
	if (col < 0) col = 8;
	this.moveCurrentTo(this.current.row, col);
};

// Move the current location to the specified new location
gameboard.prototype.moveCurrentTo = function(row, col) {
	this.current.focused = false;
	
	this.current = this.getCellAt(row, col);
	this.current.focused = true;
	main.focus = this.current;
};

// Shift the focus cell right one column.  
// Wrap around to the left if we are at the far right.
gameboard.prototype.right = function() {
	var col = this.current.col + 1;
	if (col > 8) col = 0;
	this.moveCurrentTo(this.current.row, col);
};

//
// Recursively solve the puzzle
//
// Solving the puzzle is used to create a new
// puzzle and could also be used to solve a current
// puzzle
//
// This function returns a boolean indicating whether
// the current call within the recursive stack succeeded.
// If false, the implementation will backtrack until
// something succeeds.
// 
gameboard.prototype.solve = function() {
	// Look for a cell to work with
	var c = this.findCellWithFewestPossibleValues();
	
	if (c == "Undefined") {
		// We are done...
		return true;
	} else {
		// Get the possible values for this cell
		var possible = this.getPossibleValues(c);
		if (possible.length == 0) {
			// Oops... We need to backtrack because there is no option here
			return false;
		} else {
			// Randomize the possible values.
			shuffle(possible);
			
			// Iterate through the possible values and try each of them
			// until one works.
			for (var i = 0; i < possible.length; i = i + 1) {
				c.setValue("" + possible[i]);
				
				if (this.solve()) {
					return true;
				} else {
					// We failed to find a solution. Reset the value of the
					// cell and try the next possibility
					c.setValue(" ");
				}
			}
		}
	}
};

// Shift the focus cell up one row.  
// Wrap around to the bottom if we are at the top
gameboard.prototype.up = function() {
	var row = this.current.row - 1;
	if (row < 0) row = 8;
	this.moveCurrentTo(row, this.current.col);
};

// Gameboard constructor
function gameboard() {
	board = this;
	
	// Visual aspects of the gameboard
	this.name = "gameboard";
	this.halign = "center";
	this.valign = "center"; 
	this.layout = "vertical";

	// Create a 3x3 grid of Region instances
	for (var regionrow = 0; regionrow < 3; regionrow = regionrow + 1) {
		var rowBox = new Box();
		rowBox.layout = "horizontal";
		this.children.push(rowBox);
		
		for (var regioncol = 0; regioncol < 3; regioncol = regioncol + 1) {
			this.children[regionrow].children.push(new region());
		}
	}
	
	// Create the cells and place them into the appropriate locations
	this.cells = new Array();
	for (var row = 0; row < 9; row = row + 1) {
		for (var col = 0; col < 9; col = col + 1) {
			var c = new cell(row, col);
			this.cells.push(c);
			this.getRegionAt(row, col).children[row % 3].children.push(c);
		}
	}
}
